﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace RunnerX
{
    class AnimatedSprite
    {
        #region Fields and Properties

        // Texture to be animated
        private Texture2D _2DTexture;

        // Frame rate variable controls.
        // FrameRate is the time (in seconds) that each frame is displayed.
        // Elapsed contains the elapsed time since the frame was last advanced.
        // When Elapsed is greater than FrameRate, we advance the frame and set Elapsed back to 0
        private double _frameRate = RunnerConfig.AnimatedSpriteFrameRate;
        private double _elapsed;

        // Offsets identify the upperleft corner of the first frame in the sprite.
        // Allows us to have multiple sprites on the same sheet by starting them at different locations.
        private int _frameOffsetX;
        private int _frameOffsetY;

        // Size of a given sprite sheet frame in pixels
        private int _frameWidth;
        private int _frameHeight;

        // Total number of frames in an animation. Defaults to 1 (a non animated sprite)
        private int _frameCount = 1;

        // Currently displayed frame
        private int _currentFrame = 0;

        // On-screen coordinates the sprite occupies.
        public int X { get; set; }
        public int Y { get; set; }

        public int Frame
        {
            get { return _currentFrame; }
            // Limits value to what is actually available.
            set { _currentFrame = (int) MathHelper.Clamp(value, 0, _frameCount); }
        }

        public double FrameLength
        {
            get { return _frameRate; }
            // Ensures the framerate isn't a negative number.
            set { _frameRate = Math.Max(value, 0d); }
        }

        // If this is set to false, frames in the sprite won't be updated.
        public bool IsAnimating { get; set; }

        #endregion

        #region Constructor

        public AnimatedSprite(Texture2D texture, int frameOffsetX, int frameOffsetY, int frameWidth,
            int frameHeight, int frameCount)
        {
            _2DTexture = texture;
            _frameOffsetX = frameOffsetX;
            _frameOffsetY = frameOffsetY;
            _frameWidth = frameWidth;
            _frameHeight = frameHeight;
            _frameCount = frameCount;
            IsAnimating = true;
        } 

        #endregion

        #region Update and Draw Methods

        /// <summary>
        /// Increments to the next frame to comply with the framerate. Abstractly, it is animated against the frame rate.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {
            if (!IsAnimating) return;
            // Accumulate elapsed time...
            _elapsed += (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Until it passes our frame length
            if (_elapsed > _frameRate)
            {
                // Increment current frame, wrapping back to 0 when it reaches frameCount
                _currentFrame = (_currentFrame + 1) % _frameCount;

                // Reset elapsed frame time.
                _elapsed = 0.0f;
            }
        }


        /// <summary>
        /// Draws the correct frame of the spritesheet to the screen.
        /// </summary>
        /// <param name="spriteBatch"></param>
        /// <param name="xOffset">X Offset from where the sprite thinks it is.</param>
        /// <param name="yOffset">X Offset from where the sprite thinks it is.</param>
        /// <param name="needBeginEnd">Should the sprite call its own SpriteBatch Begin and End methods.</param>
        public void Draw(SpriteBatch spriteBatch, int xOffset, int yOffset, bool needBeginEnd)
        {
            // We optionally use needBeginEnd because it allows the class to be more flexible. We don't have to be in a 
            //  spriteBatch sequence to draw. In practice, if we are drawing a lot of sprites, we don't want the additional
            //  overhead of beginning and ending a spritebatch drawing session with each sprite. It should do it all in one
            //  swoop. If we called Begin and End each sprite, it would cut into sprite drawing performance.
            if (needBeginEnd) spriteBatch.Begin();

            spriteBatch.Draw(_2DTexture, new Rectangle(X + xOffset, Y + yOffset, _frameWidth, _frameHeight),
                GetSourceRect(), Color.White);

            if (needBeginEnd) spriteBatch.End();
        }

        public void Draw(SpriteBatch spriteBatch, int xOffset, int yOffset)
        {
            Draw(spriteBatch, xOffset, yOffset, true);
        }

        #endregion

        #region Helper Functions
        private Rectangle GetSourceRect()
        {
            // Used in the Draw method to determine where on the sprite sheet (based on _urrentFrame) we will pull from when
            //  drawing our sprite. Because we have predetermined all frames of an animation must appear on the same "line"
            //  in the sprite sheet and be the same size, ScreenY, FrameWidth, and FrameHeight never need to be changed.
            return new Rectangle(_frameOffsetX + (_frameWidth * _currentFrame), _frameOffsetY, _frameWidth, _frameHeight);
        }
        #endregion
    }
}
